#include "gstsave2mp4.h"
#include "qdebug.h"

/*
 * GStreamer 0.10 Core Reference Manual:
 * https://www.freedesktop.org/software/gstreamer-sdk/data/docs/latest/gstreamer-0.10/
 *
 *
 *
 */

GstSave2Mp4::GstSave2Mp4(QObject *parent) : QObject(parent)
{

}

void GstSave2Mp4::createPipeline()
{
    /* Initialisation */
    gst_init (NULL, NULL);

    char uri[] = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";
    char location[] = "./test.mp4";

    /* Create gstreamer elements */
    pipeline     = gst_pipeline_new ("rtsp-player");
    source       = gst_element_factory_make ("uridecodebin", "source");
    audioEncoder = gst_element_factory_make("ffenc_aac", "audio-encoder");
    videoEncoder = gst_element_factory_make("x264enc", "video-encoder");
    muxer        = gst_element_factory_make("mp4mux", "muxer");
    sink         = gst_element_factory_make ("filesink", "sink");

    GstElement *queue1 = gst_element_factory_make ("queue", "queue1");
    GstElement *queue2= gst_element_factory_make ("queue", "queue2");

    if (!pipeline || !source || !audioEncoder || !videoEncoder || !muxer || !sink) {
        g_printerr ("One element could not be created.\n");
    }

    /* Set up the pipeline */

    /* we set the rtsp uri to the source element and set file path to the sink element. */
    g_object_set (G_OBJECT (source), "uri", uri, NULL);
    g_object_set (G_OBJECT (sink), "location", location, NULL);


    /* we add a message handler */
    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
    gst_object_unref (bus);
    gst_bus_add_signal_watch (bus);

    /* we add all elements into the pipeline */
    gst_bin_add_many (GST_BIN (pipeline), source, queue1, audioEncoder, queue2, videoEncoder , muxer, sink, NULL);

    g_signal_connect (bus, "message", G_CALLBACK (cb_message), this);
    g_signal_connect (source, "pad-added", G_CALLBACK (on_pad_added), queue1);
    g_signal_connect (source, "pad-added", G_CALLBACK (on_pad_added), queue2);

    if(!gst_element_link(muxer, sink)){
        g_warning("failed to link muxer and sink");
    }

    if(!gst_element_link(audioEncoder, muxer)){
        g_warning("failed to link audioEncoder and muxer");
    }

    if(!gst_element_link(queue1, audioEncoder)){
        g_warning("failed to link audioEncoder and queue1");
    }

    if(!gst_element_link(videoEncoder, muxer)){
        g_warning("failed to link videoEncoder and muxer");
    }

    if(!gst_element_link(queue2, videoEncoder)){
        g_warning("failed to link audioEncoder and queue2");
    }

    /* Set the pipeline to "playing" state*/
    g_print ("Now playing: %s\n", uri);
    gst_element_set_state (pipeline, GST_STATE_PLAYING);

    /* Iterate */
    g_print ("Running...\n");
}

void GstSave2Mp4::cb_message (GstBus *bus, GstMessage *msg, gpointer *data)
{
//    qDebug() << "cb_message";
    GstSave2Mp4 *gstSave2Mp4 = (GstSave2Mp4*)data;
    switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_ERROR: {
        GError *err;
        gchar *debug;

        gst_message_parse_error (msg, &err, &debug);
        g_print ("Error: %s\n", err->message);
        g_error_free (err);
        g_free (debug);

        gst_element_set_state (gstSave2Mp4->pipeline, GST_STATE_READY);
        break;
    }
    case GST_MESSAGE_EOS:
        qDebug()<<"EOS !!!!!!";
        /* end-of-stream */
        gst_element_set_state (gstSave2Mp4->pipeline, GST_STATE_READY);
        break;
    case GST_MESSAGE_BUFFERING: {
        gint percent = 0;

        /* If the stream is live, we do not care about buffering. */
        //      if (data->is_live) break;

        gst_message_parse_buffering (msg, &percent);
        g_print ("Buffering (%3d%%)\r", percent);
        /* Wait until buffering is complete before start/resume playing */
        //      if (percent < 100)
        //        gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
        //      else
        //        gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
        break;
    }
    case GST_MESSAGE_CLOCK_LOST:
        /* Get a new clock */
        gst_element_set_state (gstSave2Mp4->pipeline, GST_STATE_PAUSED);
        gst_element_set_state (gstSave2Mp4->pipeline, GST_STATE_PLAYING);
        break;
    case GST_MESSAGE_STATE_CHANGED:
        GstState stateOld;
        GstState stateNew;

        gst_message_parse_state_changed(msg, &stateOld, &stateNew, NULL);
        qDebug() << "STATE CHANGE: "
                 << "NAME: " << QString(GST_OBJECT_NAME (msg->src))
                 << " Old State: "  << QString(gst_element_state_get_name (stateOld))
                 << " New State: "  << QString(gst_element_state_get_name (stateNew));
        break;
    default:
        /* Unhandled message */
        break;
    }
}

void GstSave2Mp4::on_pad_added (GstElement *element,
              GstPad     *pad,
              gpointer    data)
{
    GstPad *sinkpad;
    GstElement *encoder = (GstElement *) data;
    gchar *name;
    qDebug() << "on_pad_added";
    //  name = gst_pad_get_name (pad);
    //  GstCaps *caps = gst_pad_get_caps(pad);
    //  gchar *caps_string = gst_caps_to_string(caps);
    //  g_print ("A new pad %s was created\n", name);
    //  g_print("caps:%s\n", caps_string);
    //  g_free (name);
    //  g_free(caps_string);

    /* We can now link this pad with the vorbis-decoder sink pad */
    g_print ("Dynamic pad created, linking demuxer/decoder\n");

    sinkpad = gst_element_get_static_pad (encoder, "sink");

    GstPadLinkReturn lret = gst_pad_link (pad, sinkpad);

    switch (lret) {
    case GST_PAD_LINK_OK:
        qDebug() << "on_pad_added GST_PAD_LINK_OK";
        break;
    case GST_PAD_LINK_WRONG_HIERARCHY:
        qDebug() << "on_pad_added GST_PAD_LINK_WRONG_HIERARCHY";
        break;
    case GST_PAD_LINK_WAS_LINKED:
        qDebug() << "on_pad_added GST_PAD_LINK_WAS_LINKED";
        break;

    case GST_PAD_LINK_WRONG_DIRECTION:
        qDebug() << "on_pad_added GST_PAD_LINK_WRONG_DIRECTION";
        break;
    case GST_PAD_LINK_NOFORMAT:
        qDebug() << "on_pad_added GST_PAD_LINK_NOFORMAT";
        break;
    case GST_PAD_LINK_NOSCHED:
        qDebug() << "on_pad_added GST_PAD_LINK_NOSCHED";
        break;
    case GST_PAD_LINK_REFUSED:
        qDebug() << "on_pad_added GST_PAD_LINK_REFUSED";
        break;

    }

    gst_object_unref (sinkpad);
}
